package com.scs.application.modules.svn.utils;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UtilCommon;
import com.scs.application.modules.svn.entity.SvnVersion;
import com.scs.application.modules.svn.service.SvnVersionService;
import com.scs.application.modules.sys.properties.SystemProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import javax.annotation.PostConstruct;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ Description： svn工具类
 */
@Component
@Slf4j
public class UtilSvn {

    @Autowired
    SvnVersionService svnVersionService;
    @Autowired
    private SystemProperties systemProperties;

    private static UtilSvn inst;
    //是否执行中
    private static boolean isExec = false;

    @PostConstruct
    public void init() {
        inst = this;
        inst.svnVersionService = this.svnVersionService;
        inst.systemProperties = this.systemProperties;
    }


    private static Map<String, SVNRepository> repositoryMap = new HashMap<String, SVNRepository>();

    private static Map<String, String> versionLibMap = new HashMap<String, String>();

    static {
    }

    //连接
    private static void setupLibrary(String versionLib, String url) throws SVNException {
        if (repositoryMap.get(versionLib) != null) return;
        try {
            DAVRepositoryFactory.setup();
            SVNRepositoryFactoryImpl.setup();

            FSRepositoryFactory.setup();


            repositoryMap.put(versionLib, SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url)));
            // 身份验证
            ISVNAuthenticationManager authManager;
            //使用yml配置的
            if (StringUtils.isNotBlank(UtilSvn.inst.systemProperties.getSvnpassword()) && StringUtils.isNotBlank(UtilSvn.inst.systemProperties.getSvnpassword())) {
                authManager = SVNWCUtil.createDefaultAuthenticationManager(UtilSvn.inst.systemProperties.getSvnpassword(), UtilSvn.inst.systemProperties.getSvnpassword());
            } else { //使用svn客户端配置的
                authManager = SVNWCUtil.createDefaultAuthenticationManager();
            }

            repositoryMap.get(versionLib).setAuthenticationManager(authManager);
        } catch (Exception e) {
            repositoryMap.put(versionLib, null);
            throw e;
        }
    }


    /**
     * @Description： 将svn版本信息同步到svn_version表，增量
     */
    synchronized public static String initSvnVersion(boolean throwE) throws BusinessException {
        if (isExec) return "initSvnVersion上次执行尚未结束，请稍候重试";
        isExec = true;
        List<String> msgArr = new ArrayList<String>();

        //项目分支
        String svnprojecturl = UtilSvn.inst.systemProperties.getSvnprojecturl();
        if (StringUtils.isNotBlank(svnprojecturl) && svnprojecturl.indexOf("dev") == -1 && svnprojecturl.indexOf("sit") == -1 && svnprojecturl.indexOf("release") == -1) {
            versionLibMap.put("project", svnprojecturl);
        }

        if (StringUtils.isNotBlank(UtilSvn.inst.systemProperties.getSvndevurl()) ) {
            versionLibMap.put("dev", UtilSvn.inst.systemProperties.getSvndevurl());
        }
        if (StringUtils.isNotBlank(UtilSvn.inst.systemProperties.getSvnsiturl()) ) {
            versionLibMap.put("sit", UtilSvn.inst.systemProperties.getSvnsiturl());
        }
        if (StringUtils.isNotBlank(UtilSvn.inst.systemProperties.getSvnreleaseurl()) ) {
            versionLibMap.put("release", UtilSvn.inst.systemProperties.getSvnreleaseurl());
        }

        Set keySet = versionLibMap.keySet();
        Iterator it = keySet.iterator();
        while (it.hasNext()) {
            String key = it.next() + "";
            try {
                msgArr.add(String.format("版本库：%s，新增提交记录：%s", key, _initSvnVersion(key, versionLibMap.get(key), throwE)));
            } catch (Exception e) {
                if (throwE) {
                    isExec = false;
                    throw new BusinessException(e.getMessage());
                }
            }
        }
        isExec = false;
        UtilSvn.inst.svnVersionService.updateMergeInfo();
        return StringUtils.join(msgArr, "<br>");
    }

    public static int _initSvnVersion(String versionLib, String versionUrl, boolean throwE) throws Exception {
        // 过滤条件
        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        try {
            log.info("#########UtilSvn.intSvnVersion.begin versionLib={}#########", versionLib);
            setupLibrary(versionLib, versionUrl);

            //项目分支版本，先删除版本类型是project 但是 版本库地址不同的
            if (versionLib.equals("project")) {
                UtilSvn.inst.svnVersionService.remove(Wrappers.<SvnVersion>query().eq("version_lib", versionLib).ne("version_url", versionUrl));
            }

            //日志开始时间
            Date begin;
            //日志结束时间为当前时间
            Date end = DateUtils.now();

            //数据库最大 日期
            SvnVersion svnVersionLast = UtilSvn.inst.svnVersionService.getOne(Wrappers.<SvnVersion>query().eq("version_lib", versionLib).select("submit_date").orderByDesc("submit_date").last("limit 1"), false);
            if (svnVersionLast != null) {
                begin = svnVersionLast.getSubmitDate();
            } else {
                begin = DateUtils.parse(GlobalConsts.SVNVERSION_BEGIN);
            }

            //二次校验，防止重复
            List<SvnVersion> svnVersionDbList = UtilSvn.inst.svnVersionService.list(Wrappers.<SvnVersion>query().eq("version_lib", versionLib).ge("submit_date", begin));
            Map<String, List<SvnVersion>> svnVersionMap;
            if (svnVersionDbList != null && svnVersionDbList.size() > 0) {
                svnVersionMap = svnVersionDbList.stream().collect(Collectors.groupingBy(svnVersion -> svnVersion.getVersionLib() + "_" + svnVersion.getVersionNum()));
            } else {
                svnVersionMap = new HashMap<String, List<SvnVersion>>();
            }

            final String author = "";
            long startRevision = 0;
            long endRevision = -1;//表示最后一个版本
            List<SvnVersion> svnVersionSaveList = new ArrayList<SvnVersion>();
            repositoryMap.get(versionLib).log(new String[]{""},
                    startRevision,
                    endRevision,
                    true,
                    true,
                    new ISVNLogEntryHandler() {
                        @Override
                        public void handleLogEntry(SVNLogEntry svnlogentry)
                                throws SVNException {
                            //依据提交时间进行过滤
                            if (svnlogentry.getDate().after(begin)
                                    && svnlogentry.getDate().before(end)) {
                                // 依据提交人过滤
                                if (!"".equals(author)) {
                                    if (author.equals(svnlogentry.getAuthor())) {
                                        fillResult(svnlogentry);
                                    }
                                } else {
                                    fillResult(svnlogentry);
                                }
                            }
                        }

                        public void fillResult(SVNLogEntry svnlogentry) {
                            Map<String, SVNLogEntryPath> map = svnlogentry.getChangedPaths();
                            Date submitDate = null;
                            try {
                                submitDate = DateUtils.parse(DateUtils.format(svnlogentry.getDate(), "yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss");
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }

                            //涉及到的文件
                            List<String> filePathList = new ArrayList<String>();
                            map.keySet().stream().forEach(key -> {
                                if (key.indexOf(".") != -1) {
                                    filePathList.add("文件:" + key);
                                } else {
                                    filePathList.add("目录:" + key);
                                }
                            });
                            String submitRemark = svnlogentry.getMessage();


                            SvnVersion svnVersion = new SvnVersion();
                            svnVersion.setVersionLib(versionLib).setVersionNum(svnlogentry.getRevision() + "").setVersionUrl(versionUrl)
                                    .setSubmitUser(svnlogentry.getAuthor()).setSubmitDate(submitDate)
                                    .setFileList(StringUtils.join(filePathList, "\n")).setFileNum(filePathList.size()).setSubmitRemark(svnlogentry.getMessage())
                                    .setChangeValid(false)
                            ;

                            /**
                             日志为以下格式
                             子系统类型   可选：spdpc，spdapp，spdfsd
                             #分隔符#
                             功能分类    基建|主数据|外设|业务|配送|财务|BI|无 【多个,分割】
                             #分隔符#
                             菜单        菜单名称|无
                             #分隔符#
                             需求来源    TAPD缺陷|TAPD需求|禅道任务|禅道bug|无
                             #分隔符#
                             需求来源标识 884|无 【多个,分割】
                             #分隔符#
                             需求来源链接  需求来源链接|无 【多个,分割】
                             #分隔符#
                             功能描述      功能描述|无
                             */
                            if (StringUtils.isNotBlank(submitRemark)) {
                                String[] submitRemarkSplit = submitRemark.split(GlobalConsts.SVNVERSION_LOG_SPLIT);
                                if (submitRemarkSplit != null) {
                                    //第二个参数为系统类型的时候，为最新的日志格式
                                    if (submitRemarkSplit.length > 2 && Arrays.asList("spdpc", "spdapp", "spdfsd").contains(StringUtils.trimAll(submitRemarkSplit[1]))) {
                                        if (submitRemarkSplit.length >= 7) {
                                            svnVersion.setChangeRemark(StringUtils.trim(submitRemarkSplit[0]))
                                                    .setChangeSystem(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[1]), 30))
                                                    .setChangeType(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[2]), 50))
                                                    .setChangeMenu(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[3]), 100))
                                                    .setChangeFrom(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[4]), 50))
                                                    .setChangeCode(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[5]), 50))
                                                    .setChangeUrl(StringUtils.trim(submitRemarkSplit[6]))
                                                    .setChangeValid(true)
                                            ;
                                        }
                                        if (submitRemarkSplit.length >= 8) { //包含重要程度
                                            svnVersion.setChangeImportance(StringUtils.trim(submitRemarkSplit[7]));
                                        }
                                    } else {
                                        if (submitRemarkSplit.length >= 7) {
                                            svnVersion.setChangeSystem(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[0]), 30))
                                                    .setChangeType(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[1]), 50))
                                                    .setChangeMenu(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[2]), 100))
                                                    .setChangeFrom(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[3]), 50))
                                                    .setChangeCode(UtilCommon.getStringPre(StringUtils.trim(submitRemarkSplit[4]), 50))
                                                    .setChangeUrl(StringUtils.trim(submitRemarkSplit[5]))
                                                    .setChangeRemark(StringUtils.trim(submitRemarkSplit[6]))
                                                    .setChangeValid(true)
                                            ;
                                        }

                                        if (submitRemarkSplit.length >= 8) { //包含重要程度
                                            svnVersion.setChangeImportance(StringUtils.trim(submitRemarkSplit[7]));
                                        }
                                    }
                                }
                            }
                            if (!svnVersionMap.containsKey(svnVersion.getVersionLib() + "_" + svnVersion.getVersionNum())) {
                                svnVersion.setChangeImportance(UtilCommon.getStringPre(svnVersion.getChangeImportance(), 50));
                                svnVersionSaveList.add(svnVersion);
                            }
                        }
                    });
            if (svnVersionSaveList.size() > 0) UtilSvn.inst.svnVersionService.saveBatch(svnVersionSaveList, 500);
            log.info("#########UtilSvn.intSvnVersion.end versionLib={}#########", versionLib);
            return svnVersionSaveList.size();
        } catch (Exception e) {
            log.error("#########UtilSvn.intSvnVersion.error versionLib=" + versionLib + "#########", e);
            if (throwE) throw new BusinessException("%s版本库获取日志失败，原因：%s", versionLib, e.getMessage());
            return 0;
        }
    }
}
